package search;

import helpers.DateHelper;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;

import models.OrderModel;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import admin.data.OrderData;

import com.google.appengine.api.search.Document;
import com.google.appengine.api.search.Field;
import com.google.appengine.api.search.Index;
import com.google.appengine.api.search.IndexSpec;
import com.google.appengine.api.search.ListRequest;
import com.google.appengine.api.search.ListResponse;
import com.google.appengine.api.search.Query;
import com.google.appengine.api.search.QueryOptions;
import com.google.appengine.api.search.Results;
import com.google.appengine.api.search.ScoredDocument;
import com.google.appengine.api.search.SearchServiceFactory;

public class OrderSearchEngine {

	private static Index INDEX = SearchServiceFactory.getSearchService()
			.getIndex(IndexSpec.newBuilder().setName("order"));
	private static final Logger log = Logger.getLogger(OrderSearchEngine.class
			.getName());

	public static void index(OrderModel obj) {
		try {
			Document.Builder docBuilder = Document.newBuilder();
			objectToSearchDocument(docBuilder, obj);
			Document doc = docBuilder.build();				
			INDEX.add(doc);
			System.out.println("Order - " + obj.getOrderId() + " indexed.");
		} catch (Exception e) {
			System.out.println("Error when try to index Order !");
			log.warning("Error when try to index Order !");
			log.warning(e.toString());
			e.printStackTrace();
		}
	}

	public static void update(OrderModel obj) {
		remove(obj.getOrderId());
		index(obj);
	}

	public static void remove(String id) {
		try {
			Query query = Query.newBuilder()
					.setOptions(QueryOptions.newBuilder().build())
					.build("orderId:\"" + id + "\"");
			Results<ScoredDocument> docResult = INDEX.search(query);
			if (docResult.getNumberFound() > 0) {
				for (ScoredDocument scoredDocument : docResult) {
					INDEX.remove(scoredDocument.getId());
				}
				System.out.println("Remove order index success !");
			}
		} catch (Exception e) {
			System.out.println("Error when try to remove order index !");
			log.warning("Error when try to remove order index !");
			log.warning(e.toString());
			e.printStackTrace();
		}
	}

	public static void removeAllDocument() {
		int count = 0;
		while (true) {
			List<String> docIds = new ArrayList<String>();
			// Return a set of document IDs.
			ListRequest request = ListRequest.newBuilder()
					.setReturningIdsOnly(true).build();
			ListResponse<Document> response = INDEX.listDocuments(request);
			if (response.getResults().isEmpty()) {
				break;
			}
			for (Document doc : response) {
				docIds.add(doc.getId());
				count++;
			}
			INDEX.remove(docIds);
		}
		System.out
				.println("Remove " + count + " document from Index success !");
	}

	public static void reindex() {
		int count = 0;
		OrderData orderData = new OrderData();
		orderData.setPage(1);
		while (true) {
			orderData.setLimit(488);
			List<OrderModel> list = orderData.list();
			for (OrderModel orderModel : list) {
				
				index(orderModel);
				count++;
			}
			if (list.size() < 488) {
				break;
			}
			orderData.setPage(orderData.getPage() + 1);
		}
		System.out.println("Index " + count
				+ " document from Order Table success !");
	}

	@SuppressWarnings({ "unchecked" })
	public static Document.Builder objectToSearchDocument(
			Document.Builder docBuilder, Object obj) {
		for (java.lang.reflect.Field f : obj.getClass().getDeclaredFields()) {
			f.setAccessible(true);
			if (f.getType().getSimpleName().equals("String")) {
				try {
					docBuilder.addField(Field.newBuilder().setName(f.getName())
							.setText((String) f.get(obj)));
				} catch (Exception e) {

				}
			} else if (f.getType().getSimpleName().equals("int")) {
				try {
					docBuilder.addField(Field.newBuilder().setName(f.getName())
							.setNumber(f.getInt(obj)));
				} catch (Exception e) {

				}
			} else if (f.getType().getSimpleName().equals("Date")) {
				try {
					docBuilder.addField(Field
							.newBuilder()
							.setName(f.getName())
							.setDate(
									DateHelper.removeTimeFromDate((Date) f
											.get(obj))));
				} catch (Exception e) {

				}
			} else if (f.getType().getSimpleName().equals("boolean")) {
				try {
					docBuilder.addField(Field.newBuilder().setName(f.getName())
							.setText(f.get(obj).toString()));
				} catch (Exception e) {
					e.printStackTrace();
				}
			} else if (f.getType().getSimpleName().equals("long")) {
				try {
					docBuilder.addField(Field.newBuilder().setName(f.getName())
							.setNumber(f.getLong(obj)));
				} catch (Exception e) {

				}
			} else if (f.getType().getSimpleName().equals("long")) {
				try {
					docBuilder.addField(Field.newBuilder().setName(f.getName())
							.setNumber(f.getLong(obj)));
				} catch (Exception e) {

				}
			} else if (f.getType().getSimpleName().equals("List")) {
				try {
					List<String> keys = (ArrayList<String>) f.get(obj);
					StringBuilder stringBuilder = new StringBuilder();
					stringBuilder.append("<products>");
					for (String string : keys) {
						stringBuilder.append("<product>");						
						stringBuilder.append(string);
						stringBuilder.append("</product>");
					}
					stringBuilder.append("</products>");
					try {
						docBuilder.addField(Field.newBuilder()
								.setName(f.getName())
								.setText(stringBuilder.toString()));
					} catch (Exception e) {

					}
				} catch (Exception e) {

				}
			}
		}		
		return docBuilder;
	}

	public static OrderModel documentToObjectByReflection(ScoredDocument doc) {
		OrderModel obj = new OrderModel();
		for (java.lang.reflect.Field f : obj.getClass().getDeclaredFields()) {
			f.setAccessible(true);
			if (f.getType().getSimpleName().equals("String")) {
				try {
					StringBuilder tmpString = new StringBuilder();
					tmpString.append(doc.getOnlyField(f.getName()).getText());
					f.set(obj, tmpString.toString());
				} catch (Exception e) {

				}
			} else if (f.getType().getSimpleName().equals("int")) {
				try {
					f.set(obj, doc.getOnlyField(f.getName()).getNumber()
							.intValue());
				} catch (Exception e) {

				}
			} else if (f.getType().getSimpleName().equals("Date")) {
				try {
					f.set(obj, doc.getOnlyField(f.getName()).getDate());
				} catch (Exception e) {

				}
			} else if (f.getType().getSimpleName().equals("boolean")) {
				try {
					f.set(obj,
							doc.getOnlyField(f.getName()).getText()
									.equals("true") ? true : false);
				} catch (Exception e) {

				}
			} else if (f.getType().getSimpleName().equals("long")) {
				try {
					f.set(obj, doc.getOnlyField(f.getName()).getNumber()
							.longValue());
				} catch (Exception e) {

				}
			} else if (f.getType().getSimpleName().equals("List")) {
				try {
					List<String> listProduct = new ArrayList<String>();
					org.jsoup.nodes.Document document = Jsoup.parse(doc
							.getOnlyField(f.getName()).getText());
					Elements tmp;
					tmp = document.select("product");
					for (Element element : tmp) {
						listProduct.add(element.html());
					}
					f.set(obj, listProduct);
				} catch (Exception e) {

				}
			}
		}
		return obj;
	}
}
